package com.wenlin.myproduction.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

import com.wenlin.myproduction.BuildConfig;
import com.wenlin.myproduction.R;


/**
 * 图片数字控件
 *
 * @author cwl
 * @desc 主要用来显示等级升级效果
 * @Email 377979485@qq.com
 * @time 2023/10/21  10:16
 */
public class NumberView extends View {
    public static final int[] NUMBER_PIC_RES_IDS = {R.mipmap.common_icon_level_0, R.mipmap.common_icon_level_1, R.mipmap.common_icon_level_2, R.mipmap.common_icon_level_3,
            R.mipmap.common_icon_level_4, R.mipmap.common_icon_level_5, R.mipmap.common_icon_level_6, R.mipmap.common_icon_level_7,
            R.mipmap.common_icon_level_8, R.mipmap.common_icon_level_9};
    private static final long ANIM_DUR = 800;
    private final float picWidth = dpToPx(79);
    private final float picHeight = dpToPx(85);
    private int viewWidth, viewHeight;
    private int oldNumber, number;//新旧数字
    private String oldNumberStr, newNumberStr;//新旧数字字符串
    private Paint paint;
    private Bitmap[] arrBitmap = new Bitmap[10];
    private float distance;//动画：移动的距离
    private ValueAnimator valueAnimator;//动画执行者
    private boolean isRelease;//是否释放资源了，如果释放了则不进行绘制等操作了
    private int maxLength, maxChangeLength;//数字组成的字符串的最大长度，数字新旧变化最大改变步长，如0升到9步长为9，又如7升到8步长为1。
    private boolean needRequestLayout;//是否需要重新测量
    private boolean newNumberSmaller;//新的数字更小

    public NumberView(Context context) {
        this(context, null);
    }

    public NumberView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * 初始化画笔等
     */
    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);

        //初始化位图
        for (int i = 0; i < NUMBER_PIC_RES_IDS.length; i++) {
            getBitmapFromCache(i);
        }

        //调试时默认显示0
        if (BuildConfig.DEBUG) jumpTo(number);
    }

    /**
     * 设置数值，没有动画
     *
     * @param theNumber 数值
     */
    public void jumpTo(int theNumber) {
        if (isRelease) return;
        oldNumber = number;
        number = theNumber;
        if (number < 0) number = 0;
        computeMaxChangeLength();
        newNumberSmaller = number < oldNumber;
        oldNumber = number;
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator.removeAllUpdateListeners();
            valueAnimator.removeAllListeners();
            valueAnimator = null;
        }
        needRequestLayout = true;//必定重新计算长度
        invalidate();
    }

    /**
     * 设置新的数字，如果比旧的数字刚好多1，则显示动画，否则直接设置数字
     *
     * @param theNumber 数字
     */
    public void runTo(int theNumber) {
        if (isRelease) return;
        oldNumber = number;
        number = theNumber;
        if (number < 0) number = 0;
        computeMaxChangeLength();
        newNumberSmaller = number < oldNumber;
        oldNumber = number;
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator.removeAllUpdateListeners();
            valueAnimator.removeAllListeners();
            valueAnimator = null;
        }
        startAnim();
        postInvalidate();
    }

    /**
     * 获取每个数位改变次数的最大值。比如099 -> 105，则改动次数分别为1(0~1：0、1)，1(9~0：9、0),6(9~5：9、0、1、2、3、4、5)
     */
    private void computeMaxChangeLength() {
        needRequestLayout = newNumberStr != null && newNumberStr.startsWith("0") || String.valueOf(number).length() != String.valueOf(oldNumber).length();
        StringBuilder oldSB = new StringBuilder(String.valueOf(oldNumber));
        StringBuilder newSB = new StringBuilder(String.valueOf(number));
        int oldLength = oldSB.length();
        int newLength = newSB.length();
        maxLength = Math.max(oldLength, newLength);
        //新旧两个数通过前面加0的方式，凑成两个一样长的字符串
        if (oldLength < maxLength) {
            for (int i = oldLength; i < maxLength; i++) {
                oldSB.insert(0, "0");
            }
        } else if (newLength < maxLength) {
            for (int i = newLength; i < maxLength; i++) {
                newSB.insert(0, "0");
            }
        }
        oldNumberStr = oldSB.toString();
        newNumberStr = newSB.toString();
        maxChangeLength = 0;//新旧两个数字的相应数位的改变次数之中的最大值
        int oldNumberTemp;
        int newNumberTemp;
        int delta;
        try {
            for (int i = maxLength - 1; i >= 0; i--) {
                oldNumberTemp = Integer.parseInt(getStrIndexOf(i, oldNumberStr));
                newNumberTemp = Integer.parseInt(getStrIndexOf(i, newNumberStr));
                if (newNumberSmaller) {
                    if (oldNumberTemp < newNumberTemp) oldNumberTemp += 10;
                    delta = oldNumberTemp - newNumberTemp;
                } else {
                    if (newNumberTemp < oldNumberTemp) newNumberTemp += 10;
                    delta = newNumberTemp - oldNumberTemp;
                }
                maxChangeLength = Math.max(delta, maxChangeLength);
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取单个数字，如果原数大于10，则取个位数字，如果小于0，则取加10之后的数字
     *
     * @param n 原数
     * @return 所求
     */
    private int getSingleFormatNumber(int n) {
        if (n >= 10) n -= 10;
        if (n < 0) n += 10;
        return n;
    }

    /**
     * 获取指定位置的字符
     *
     * @param index 位置脚标
     * @param str   字符串
     * @return 所求
     */
    private String getStrIndexOf(int index, String str) {
        if (str != null && str.length() > index) {
            return str.substring(index, index + 1);
        }
        return "";
    }

    /**
     * 开始动画倒计时
     */
    private void startAnim() {
        if (isRelease) return;
        valueAnimator = ValueAnimator.ofFloat(0, picHeight * maxChangeLength);
        valueAnimator.addUpdateListener(animation -> {
            distance = (float) animation.getAnimatedValue();
            invalidate();
        });
        valueAnimator.setDuration(ANIM_DUR);
        valueAnimator.start();
    }

    /**
     * 从缓存中获取Bitmap图片
     *
     * @param s 数字字符
     * @return 所求
     */
    private Bitmap getBitmapFromCache(String s) {
        int number = 0;
        try {
            number = Integer.parseInt(s);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        return getBitmapFromCache(number);
    }

    /**
     * 从缓存中获取Bitmap图片
     *
     * @param number 数字
     * @return 所求
     */
    private Bitmap getBitmapFromCache(int number) {
        Bitmap bitmap = arrBitmap[number];
        if (bitmap == null) {
            bitmap = BitmapFactory.decodeResource(getResources(), NUMBER_PIC_RES_IDS[number]);
            bitmap = handleBitmap(bitmap, picWidth, picHeight);
            arrBitmap[number] = bitmap;
        }
        return bitmap;
    }

    /**
     * 生成新的Bitmap图片
     *
     * @return 所求
     */
    private Bitmap handleBitmap(Bitmap bitmap, float newWidth, float newHeight) {
        Matrix matrix = new Matrix();//创建一个处理图片的类
        int width = bitmap.getWidth();//获取图片本身的大小(宽)
        int height = bitmap.getHeight();//获取图片本身的大小(高)
        float wS = newWidth / width;//缩放比---->这块注意这个是新的宽度/高度除以旧的宽度
        float hS = newHeight / height;//缩放比---->这块注意这个是新的宽度/高度除以旧的宽度
        matrix.postScale(wS, hS);//这块就是处理缩放的比例
        //matrix.setScale(sX,sY);//缩放图片的质量sX表示宽0.5就代表缩放一半,sX同样
        return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
    }

    /**
     * 释放资源
     */
    public void release() {
        isRelease = true;
        //释放Bitmap图片资源
        if (arrBitmap != null && arrBitmap.length > 0) {
            for (int i = 0; i < arrBitmap.length; i++) {
                Bitmap bitmap = arrBitmap[i];
                if (bitmap != null && !bitmap.isRecycled()) {
                    bitmap.recycle();
                    bitmap = null;
                    arrBitmap[i] = null;
                }
            }
        }
        arrBitmap = null;
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator.removeAllUpdateListeners();
            valueAnimator.removeAllListeners();
            valueAnimator = null;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isRelease) return;
        if (number < 0) return;
        if (needRequestLayout) {
            requestLayout();
            needRequestLayout = false;
        }
        for (int i = maxLength - 1; i >= 0; i--) {//这里的i表示数位（个位、十位）
            String oldOne = getStrIndexOf(i, oldNumberStr);
            String newOne = getStrIndexOf(i, newNumberStr);
            if (valueAnimator != null && valueAnimator.isRunning()) {
                if (newOne.equals(oldOne)) {//没有变动的数位保持不动
                    canvas.drawBitmap(getBitmapFromCache(newOne), i * picWidth + getOffset(i), 0, paint);
                } else {//有变动的数位开始动画
                    int oldN = Integer.parseInt(oldOne);
                    int newN = Integer.parseInt(newOne);
                    int delta;//各数位上变动前后的数字滑动步数：0向上滑动到9需9步。0若向下滑动到9只需1步
                    if (newNumberSmaller) {
                        if (oldN < newN) oldN += 10;
                        delta = oldN - newN;
                    } else {
                        if (newN < oldN) newN += 10;
                        delta = newN - oldN;
                    }
                    for (int j = 0; j < delta; j++) {
                        float y1, y2;
                        if (newNumberSmaller) {//如果新数更小
                            y1 = -j * picHeight + (float) delta / (float) maxChangeLength * distance;//当前数字图片的y坐标
                            y2 = y1 - picHeight;//下一张数字图片的y坐标
                            canvas.drawBitmap(getBitmapFromCache(getSingleFormatNumber(oldN - j)), i * picWidth + getOffset(i), y1, paint);
                            canvas.drawBitmap(getBitmapFromCache(getSingleFormatNumber(oldN - j - 1)), i * picWidth + getOffset(i), y2, paint);
                        } else {//如果新数更大
                            y1 = j * picHeight - (float) delta / (float) maxChangeLength * distance;//当前数字图片的y坐标
                            y2 = y1 + picHeight;//下一张数字图片的y坐标
                            canvas.drawBitmap(getBitmapFromCache(getSingleFormatNumber(oldN + j)), i * picWidth + getOffset(i), y1, paint);
                            canvas.drawBitmap(getBitmapFromCache(getSingleFormatNumber(oldN + j + 1)), i * picWidth + getOffset(i), y2, paint);
                        }
                    }
                }
            } else {//没有执行动画的时候，比如使用jumpTo()，直接显示数字就完了
                canvas.drawBitmap(getBitmapFromCache(newOne), i * picWidth + getOffset(i), 0, paint);
            }
        }
        //解决runTo一个以零开头的数字时，零依然显示的问题
        if (!(valueAnimator != null && valueAnimator.isRunning())
                && newNumberStr != null && newNumberStr.startsWith("0"))
            jumpTo(number);
    }

    /**
     * 为图片增加偏移值，让其两边的图片往中间凑近一点
     *
     * @param i 从左到右第几张图片的脚标
     * @return 偏移值
     */
    private float getOffset(int i) {
        return (newNumberStr.length() == 2 || newNumberStr.length() == 3) && (i == 0 || i == newNumberStr.length() - 1) ?
                (i == 0 ? 1 : -1) * dpToPx(newNumberStr.length() == 2 ? 7 : 14) : 0;//cwl 2023/10/21/021 10:18:这个7是根据图片试出来的
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
    }

    /**
     * 获取宽或者高
     *
     * @param measureSpec 测量单位
     * @param isWidSpec   是宽还是高
     * @return 所求
     */
    private int measure(int measureSpec, boolean isWidSpec) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            viewWidth = (int) (String.valueOf(number).length() * picWidth);
            viewHeight = (int) picHeight;
            result = isWidSpec ? viewWidth : viewHeight;
            if (mode == MeasureSpec.AT_MOST) result = Math.min(result, size);
        }
        return result;
    }

    @Override
    protected void onDetachedFromWindow() {
        release();//测试了一下，会在Activity关闭3到5秒内执行这个方法。
        super.onDetachedFromWindow();
    }

    /**
     * 转化长度单位
     *
     * @param dp dp长度
     * @return 对应的px长度
     */
    private float dpToPx(float dp) {
        float scale = getResources().getDisplayMetrics().density;
        return dp * scale;
    }
}
